﻿#region License
/* ---------------------------------------------------------------- *
 *
 * This file is part of the Xcoordination Application Space
 * ("XcoAppSpace") http://www.xcoordination.com
 *
 * Copyright (C) 2009 Xcoordination http://www.xcoordination.com
 *
 * XcoAppSpace is free software; you can redistribute it and/or
 * modify it under the terms of version 2 of the GNU General
 * Public License as published by the Free Software Foundation.
 *
 * XcoAppSpace is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program. If not, see http://www.gnu.org/licenses/
 * or write to the Free Software Foundation, Inc., 51 Franklin
 * Street, Fifth Floor, Boston, MA 02110-1301, USA.
 *
 * ---------------------------------------------------------------- */
#endregion

using System;
using System.Collections.Generic;
using System.Threading;
using Microsoft.Ccr.Core;

namespace XcoAppSpaces.Core
{
	/// <summary>
	/// DispatcherQueue extension methods for simpler installation of port receivers.
	/// </summary>
	public static class DispatcherQueueExtensions
	{
		#region Receive & ReceiveSequentially
		/// <summary>
		/// Receive messages from the given port. Whenever an element is posted to the port, the given handler is called.
		/// </summary>
		/// <typeparam name="TMessage">The mesage type of the port.</typeparam>
		/// <param name="dpq">The dispatcher queue.</param>
		/// <param name="port">The port from which the messages should be received.</param>
		/// <param name="handler">The method to be called when a message is received from the port.</param>
        public static void Receive<TMessage>(this DispatcherQueue dpq, Port<TMessage> port, Handler<TMessage> handler)
        {
            PortReceiveHelpers.InstallConcurrentReceiver(port, dpq, handler);
        }
#if !PocketPC
		/// <summary>
		/// Receive messages from the given port. Whenever an element is posted to the port, the given handler is called.
		/// </summary>
		/// <typeparam name="TMessage">The mesage type of the port.</typeparam>
		/// <param name="dpq">The dispatcher queue.</param>
		/// <param name="port">The port from which the messages should be received.</param>
		/// <param name="handler">The method to be called when a message is received from the port.</param>
		/// <param name="syncContext">Specifies the synchronization context that is used for processing the messages.</param>
        public static void Receive<TMessage>(this DispatcherQueue dpq, Port<TMessage> port, Handler<TMessage> handler, SyncContext syncContext)
        {
			PortReceiveHelpers.InstallConcurrentReceiver(port, dpq, handler, syncContext);
        }
#endif
		/// <summary>
		/// Receive messages from the given port. Whenever an element is posted to the port, the given handler is called.
		/// Messages are received sequentially, meaning that the next message is received only after processing of the
		/// previous one has been finished.
		/// </summary>
		/// <typeparam name="TMessage">The mesage type of the port.</typeparam>
		/// <param name="dpq">The dispatcher queue.</param>
		/// <param name="port">The port from which the messages should be received.</param>
		/// <param name="handler">The method to be called when a message is received from the port.</param>
		public static void ReceiveSequentially<TMessage>(this DispatcherQueue dpq, Port<TMessage> port, Handler<TMessage> handler)
        {
			PortReceiveHelpers.InstallSequentialReceiver(port, dpq, handler);
        }

#if !PocketPC
		/// <summary>
		/// Receive messages from the given port. Whenever an element is posted to the port, the given handler is called.
		/// Messages are received sequentially, meaning that the next message is received only after processing of the
		/// previous one has been finished.
		/// </summary>
		/// <typeparam name="TMessage">The mesage type of the port.</typeparam>
		/// <param name="dpq">The dispatcher queue.</param>
		/// <param name="port">The port from which the messages should be received.</param>
		/// <param name="handler">The method to be called when a message is received from the port.</param>
		/// <param name="syncContext">Specifies the synchronization context that is used for processing the messages.</param>
		public static void ReceiveSequentially<TMessage>(this DispatcherQueue dpq, Port<TMessage> port, Handler<TMessage> handler, SyncContext syncContext)
        {
			PortReceiveHelpers.InstallSequentialReceiver(port, dpq, handler, syncContext);
		}
#endif

		/// <summary>
		/// Receive messages from the given port. Whenever an element is posted to the port, the given handler is called.
		/// </summary>
		/// <typeparam name="TMessage">The mesage type of the port.</typeparam>
		/// <param name="dpq">The dispatcher queue.</param>
		/// <param name="port">The port from which the messages should be received.</param>
		/// <param name="handler">The method to be called when a message is received from the port.</param>
		public static void ReceiveWithIterator<TMessage>(this DispatcherQueue dpq, Port<TMessage> port, IteratorHandler<TMessage> handler)
		{
			PortReceiveHelpers.InstallIteratortReceiver(port, dpq, handler);
		}
		#endregion

		#region Receive & ReceiveSequentially with Port creation
		/// <summary>
		/// Creates a port and binds it to the given handler for receiving messages. Whenever an element is posted to 
		/// the port, the given handler is called.
		/// </summary>
		/// <typeparam name="T">The message type of the port.</typeparam>
		/// <param name="dpq">The dispatcher queue.</param>
		/// <param name="handler">The method to be called when a message is received from the port.</param>
		/// <returns>The created port.</returns>
		public static Port<T> Receive<T>(this DispatcherQueue dpq, Handler<T> handler)
		{
			var port = new Port<T>();
			dpq.Receive(port, handler);
			return port;
		}

		/// <summary>
		/// Creates a port and binds it to the given handler for receiving messages. Whenever an element is posted to 
		/// the port, the given handler is called. Messages are received sequentially, meaning that the next message 
		/// is received only after processing of the previous one has been finished.
		/// </summary>
		/// <typeparam name="T">The message type of the port.</typeparam>
		/// <param name="dpq">The dispatcher queue.</param>
		/// <param name="handler">The method to be called when a message is received from the port.</param>
		/// <returns>The created port.</returns>
		public static Port<T> ReceiveSequentially<T>(this DispatcherQueue dpq, Handler<T> handler)
		{
			var port = new Port<T>();
			dpq.ReceiveSequentially(port, handler);
			return port;
		}

#if !PocketPC
		/// <summary>
		/// Creates a port and binds it to the given handler for receiving messages. Whenever an element is posted to 
		/// the port, the given handler is called.
		/// </summary>
		/// <typeparam name="T">The message type of the port.</typeparam>
		/// <param name="dpq">The dispatcher queue.</param>
		/// <param name="handler">The method to be called when a message is received from the port.</param>
		/// <param name="syncContext">Specifies the synchronization context that is used for processing the messages.</param>
		/// <returns>The created port.</returns>
		public static Port<T> Receive<T>(this DispatcherQueue dpq, Handler<T> handler, SyncContext syncContext)
		{
			var port = new Port<T>();
			dpq.Receive(port, handler, syncContext);
			return port;
		}

		/// <summary>
		/// Creates a port and binds it to the given handler for receiving messages. Whenever an element is posted to 
		/// the port, the given handler is called. Messages are received sequentially, meaning that the next message 
		/// is received only after processing of the previous one has been finished.
		/// </summary>
		/// <typeparam name="T">The message type of the port.</typeparam>
		/// <param name="dpq">The dispatcher queue.</param>
		/// <param name="handler">The method to be called when a message is received from the port.</param>
		/// <param name="syncContext">Specifies the synchronization context that is used for processing the messages.</param>
		/// <returns>The created port.</returns>
		public static Port<T> ReceiveSequentially<T>(this DispatcherQueue dpq, Handler<T> handler, SyncContext syncContext)
		{
			var port = new Port<T>();
			dpq.ReceiveSequentially(port, handler, syncContext);
			return port;
		}
#endif
		#endregion

		#region ReceiveJoined
		/// <summary>
		/// Receive messages from the given ports. Whenever an element is available in both ports, the given handler is called.
		/// </summary>
		/// <typeparam name="T1">The mesage type of port 1.</typeparam>
		/// <typeparam name="T2">The mesage type of port 2.</typeparam>
		/// <param name="dpq">The dispatcher queue.</param>
		/// <param name="port1">First port from which the messages should be received.</param>
		/// <param name="port2">Second port from which the messages should be received.</param>
		/// <param name="handler">The method to be called when a message is received from both ports.</param>
		public static void ReceiveJoined<T1, T2>(this DispatcherQueue dpq, Port<T1> port1, Port<T2> port2, Handler<T1, T2> handler)
		{
			PortReceiveHelpers.InstallJoinReceiver(port1, port2, dpq, handler);
		}

#if !PocketPC
		/// <summary>
		/// Receive messages from the given ports. Whenever an element is available in both ports, the given handler is called.
		/// </summary>
		/// <typeparam name="T1">The mesage type of port 1.</typeparam>
		/// <typeparam name="T2">The mesage type of port 2.</typeparam>
		/// <param name="dpq">The dispatcher queue.</param>
		/// <param name="port1">First port from which the messages should be received.</param>
		/// <param name="port2">Second port from which the messages should be received.</param>
		/// <param name="handler">The method to be called when a message is received from both ports.</param>
		/// <param name="syncContext">Specifies the synchronization context that is used for processing the messages.</param>
		public static void ReceiveJoined<T1, T2>(this DispatcherQueue dpq, Port<T1> port1, Port<T2> port2, Handler<T1, T2> handler, SyncContext syncContext)
		{
			PortReceiveHelpers.InstallJoinReceiver(port1, port2, dpq, handler, syncContext);
		}
#endif
		#endregion

		#region ReceiveChoice
		/// <summary>
		/// When a messages is received from one of the given ports, the specific handler is called. The choice is taken only for
		/// the first element to arrive in one of the two ports.
		/// </summary>
		/// <typeparam name="T1">The mesage type of port 1.</typeparam>
		/// <typeparam name="T2">The mesage type of port 2.</typeparam>
		/// <param name="dpq">The dispatcher queue.</param>
		/// <param name="port1">First port for the choice.</param>
		/// <param name="port2">Second port for the choice.</param>
		/// <param name="handler1">The method to be called when a message arrives in the first port.</param>
		/// <param name="handler2">The method to be called when a message arrives in the second port.</param>
		public static void ReceiveChoice<T1, T2>(this DispatcherQueue dpq, Port<T1> port1, Port<T2> port2, Handler<T1> handler1, Handler<T2> handler2)
		{
			PortReceiveHelpers.InstallChoiceReceiver(port1, port2, dpq, handler1, handler2);
		}

#if !PocketPC
		/// <summary>
		/// When a messages is received from one of the given ports, the specific handler is called. The choice is taken only for
		/// the first element to arrive in one of the two ports.
		/// </summary>
		/// <typeparam name="T1">The mesage type of port 1.</typeparam>
		/// <typeparam name="T2">The mesage type of port 2.</typeparam>
		/// <param name="dpq">The dispatcher queue.</param>
		/// <param name="port1">First port for the choice.</param>
		/// <param name="port2">Second port for the choice.</param>
		/// <param name="handler1">The method to be called when a message arrives in the first port.</param>
		/// <param name="handler2">The method to be called when a message arrives in the second port.</param>
		/// <param name="syncContext">Specifies the synchronization context that is used for processing the messages.</param>
		public static void ReceiveChoice<T1, T2>(this DispatcherQueue dpq, Port<T1> port1, Port<T2> port2, Handler<T1> handler1, Handler<T2> handler2, SyncContext syncContext)
		{
			PortReceiveHelpers.InstallChoiceReceiver(port1, port2, dpq, handler1, handler2, syncContext);
		}
#endif
		#endregion

		/// <summary>
		/// Creates a timed action with the given dispatcher queue, interval and action.
		/// </summary>
		/// <param name="dpq">The dispatcher queue.</param>
		/// <param name="interval">Timer interval.</param>
		/// <param name="action">The action that should be invoked.</param>
		/// <returns>The created XcoTimedAction object. Call Invoke() or InvokeDelayed() to start the timed action. </returns>
		public static XcoTimedAction CreateTimedAction(this DispatcherQueue dpq, TimeSpan interval, Func<XcoTimedAction.Continuation> action)
		{
			return new XcoTimedAction(dpq, interval, action);
		}
	}
}
